3. Configure a Tenant Resolver

A tenant resolver is the mechanism that figures out who the current tenant is. The plugin ships with two ways of doing this, and you can also implement your own.

3.1 DNS Resolver Set Up

The plugin reads a property named "tenant.resolver.type" to know how to resolve the current tenant. The DNS resolver is the default resolver for the plugin, but this is how you would specify it in Config.groovy:

tenant { 
    resolver.type = "request" 
}

Mapping subdomains using Config.groovy

The easiest (and default) behavior is to simply map subdomains (or domains) to tenantIds in Config.groovy. Whenever a request comes in for a matching DNS entry, that tenantId will be set. Managing DNS is a pain, but you can easily add a wildcard DNS entry, then allocate subdomains on the fly without any propagation or updating to/from the DNS server:

tenant {
    resolver.request.dns.type = "config"  //This is the default
    domainTenantMap {
        client1.myco.com = 1
        client2.com = 2
        client3.myco.com = 3
    }
}

Mapping subdomains using the database

Mapping subdomains in Config.groovy works great if your tenant mapping is static. However, if you want customers to be able to sign up automatically online, you can use a database-based tenant resolver by adding the following to your Config.groovy:

tenant { 
    resolver.request.dns.type = "db" 
}

The plugin provides a command to install a domain class that will be used to store the dns/tenant mappings. Adding rows to the DomainTenantMap table will add those mappings on-the-fly (no restart required)

grails create-dns-map

Security and TenantId

One advantage of identifying tenants by domain name is that any session cookies will be unique per domain, helping with security. This means that if a user authenticates (and gets an authentication cookie), if he changes the domain he's using to access the application, he won't stay logged on because the cookie for the old domain won't apply.

This does have a loophole, though. Because the cookie lives on the client browser, it may be possible for the user to inspect the cookie, and copy any values such as jsessionid to a cookie for a different domain, thus gaining access to another tenant's data. It really depends on how secure your cookie is (does an authentication token only work for one domain?)

The multi-tenant plugin uses a very simple messaging system from the falcone-util plugin. Whenever the tenantId changes, a message is published with the new and old tenantIds. You can use this event to perform any necessary checks, and clean up any resources you don't want around when the tenantId changes. Example as follows:

//A sample class that might contain an authenticated user
class AuthenticatedUser {

//Wire in the eventBroker bean - this is where events are published from and subscribed to GroovyEventBroker eventBroker

public AuthenticatedUser() {

//Somewhere in your code, subscribe to the tenantChanged event //Call subscribe method on eventBroker, provide a closure as an event handler

eventBroker.subscribe("tenantChanged") { event, broker -> if(this.tenantId != event.newTenantId) { this.logout() } } } }

3.2 Acegi Resolver Set Up

To make the multi-tenant plugin work with acegi, you have to install an additional plugin

grails install-plugin multi-tenant-acegi

Config.groovy

Your tenant resolver property should look like this:

tenant {
    resolver.type = "acegi"
}

User domain class

Whichever User domain class you are using with the acegi plugin should have a property on it called "userTenantId". You will have to manually set this value for each user correctly. Note that you should not annotate your User domain class with MultiTenant.

Role and Requestmap domain class

Note that Role and Requestmap domain classes will keep annotated with MultiTenant, the same as the other domain classes.

3.3 Custom Tenant Resolver

If the domain-name based approach doesn't work for you, you can implement your own TenantResolver by implementing the TenantResolver interface and then adding that class to your spring config:

beans {
    //Make sure to import!
    tenantResolver(MyCustomTenantResolver)
}

3.4 Manually changing tenant

Occasionally, you will need to perform data operations for a specific tenant. For example, if you have a signup form on your website and you need to create a bunch of default records for the new tenant. There is a utility class you can use to execute a block of code for a specific tenant:

import grails.plugin.multitenant.core.util.TenantUtils

...

//Everything in the following block will be applied to tenantId=15 TenantUtils.doWithTenant(15) { Contact c = new Contact(firstName:"Bob") c.save() //This record will be saved to tenantId=15 }

//Now the tenantId will be set back to what it was before

...

3.5 Tenant Status Checker

You will probably want a way to verify that each tenant has access to their application. If you add a bean to spring named "tenantStatusChecker" that implements the "TenantStatusChecker" interface grails.plugin.multitenant.core.TenantStatusChecker, then this plugin will automatically validate each request and redirect to /noaccess.gsp if the tenant doesn't have access.

This is pretty clunky and we have plans to make it much better in the future…